

"""
.. sidebar:: **FunctionLibrary.py**

    This file contains a decorator to turn python function into a node.
    And base class for function library.
    The main idea is to use function arguments as input and output pins.


.. py:function:: IMPLEMENT_NODE(func=None, returns={}, meta={}, nodeType=NodeTypes.Pure)

Detailed description
====================

We use this function as decorator in 100% cases.
See :file:`PyFlow/Packages/PyFlowBase/FunctionLibraries` content for plenty of examples

Arguments
---------

**func**

    Function to be annotated

**returns**

    Value  of this argument is tuple with 2 or 3 elements or None.
    First element is pin data type.
    Second - default value.
    Third element is :term:`pin specifiers`

.. seealso:: :meth:`~PyFlow.Core.NodeBase.NodeBase.createInputPin`
             :meth:`~PyFlow.Core.NodeBase.NodeBase.createOutputPin`
             :class:`~PyFlow.Core.PinBase.PinBase`

**meta**

    Value of this argument is :term:`node meta`

**nodeType**

    Value of this argument is :class:`~PyFlow.Core.Common.NodeTypes`. If :attr:`~PyFlow.Core.Common.NodeTypes.Callable` specified
    input and output exec pins will be created.

Examples:
::

    @IMPLEMENT_NODE(returns=('IntPin', 0), meta={NodeMeta.CATEGORY: 'GenericTypes', NodeMeta.KEYWORDS: []})
    def makeInt(i=('IntPin', 0)):
        return i

    @IMPLEMENT_NODE(returns=('FloatPin', 0.0, {PinSpecifiers.ENABLED_OPTIONS: PinOptions.AlwaysPushDirty}))
    def clock():
        return time.processor_time()


.. glossary::

    pin specifiers
        dict that describes different pin options and attributes to be considered on generation

        Following key-value pairs allowed:

        >>> (PinSpecifiers.SUPPORTED_DATA_TYPES : list)
        >>> (PinSpecifiers.CONSTRAINT: None)
        >>> (PinSpecifiers.STRUCT_CONSTRAINT: None)
        >>> (PinSpecifiers.ENABLED_OPTIONS: None)
        >>> (PinSpecifiers.DISABLED_OPTIONS: None)
        >>> (PinSpecifiers.INPUT_WIDGET_VARIANT: "DefaultWidget")
        >>> (PinSpecifiers.DESCRIPTION: str)
        >>> (PinSpecifiers.VALUE_LIST: [str])
        >>> (PinSpecifiers.VALUE_RANGE: (int|float, int|float))
        >>> (PinSpecifiers.DRAGGER_STEPS: [int|float])

        Value list is specific for string pins. If Specified - enum input widget will be created for this pin.
        If value range is specified, slider will be created in property view instead of value box.
        Dragger steps is a list of values which will be used in value dragger (middle mouse button).


    node meta
        dict that describes different node options and attributes to be considered on generation

        Following key-value pairs allowed:

        >>> ("Category" : str)
        >>> ("Keywords" : [str])
        >>> ("CacheEnabled" : bool)

"""

# 从 inspect 模块导入获取函数参数和成员信息的函数
from inspect import getfullargspec, getmembers, isfunction

# 从 src 框架的 Core.Common 模块导入所需的常量和类
from src.Core.Common import *

# 定义一个空字典，用于作为默认参数值
empty = {}


def IMPLEMENT_NODE(
    func=None,
    returns=empty,
    meta={NodeMeta.CATEGORY: "Default", NodeMeta.KEYWORDS: []},
    nodeType=NodeTypes.Pure,
):
    """
    用于将 Python 函数转换为节点的装饰器。

    :param func: 要被注解的函数，默认为 None
    :param returns: 函数的返回信息，默认为空字典。可以是包含 2 或 3 个元素的元组或 None
    :param meta: 节点的元信息，默认为包含默认分类和空关键字列表的字典
    :param nodeType: 节点的类型，默认为 NodeTypes.Pure
    :return: 装饰后的函数
    """
    def wrapper(func):
        """
        装饰器的包装函数，用于为函数添加注解信息。

        :param func: 要被装饰的函数
        :return: 带有注解信息的函数
        """
        # 获取函数原有的注解信息，如果没有则初始化为空字典
        func.__annotations__ = getattr(func, "__annotations__", {})
        # 为函数添加节点类型的注解信息
        func.__annotations__["nodeType"] = nodeType

        # 如果 meta 不是空字典，则为函数添加元信息的注解
        if not meta == empty:
            func.__annotations__["meta"] = meta

        # 如果 returns 不是空字典，则为函数添加返回信息的注解
        if not returns == empty:
            func.__annotations__["return"] = returns

        # 获取函数的默认参数值
        defaults = func.__defaults__
        if defaults:
            # 获取函数的完整参数信息
            spec = getfullargspec(func)
            # 遍历有默认值的参数
            for i, name in enumerate(spec.args[-len(defaults):]):
                # 如果默认值的长度小于 1 或者第一个元素为空，则跳过
                if len(defaults[i]) < 1 or defaults[i][0] is empty:
                    continue
                # 为函数添加参数的注解信息
                func.__annotations__[name] = defaults[i]
        return func

    # 如果 returns 为空字典，则直接返回装饰后的函数
    if returns == empty:
        return wrapper(func)
    return wrapper


class FunctionLibraryBase(object):
    """
    函数库的基类，用于管理函数库中的函数。

    :param packageName: 函数库所属的包名
    """

    def __init__(self, packageName):
        """
        初始化函数库基类。

        :param packageName: 函数库所属的包名
        """
        # 调用父类的构造函数
        super(FunctionLibraryBase, self).__init__()
        # 用于存储函数库中的函数
        self.__foos = {}
        # 遍历当前对象的所有成员，筛选出函数
        for name, function in getmembers(self, isfunction):
            # 为函数添加包名的注解信息
            function.__annotations__["packageName"] = packageName
            # 为函数添加所属库名的注解信息
            function.__annotations__["lib"] = self.__class__.__name__
            # 将函数添加到函数库中
            self.__foos[name] = function

    def getFunctions(self):
        """
        获取函数库中存储的所有函数。

        :return: 包含函数库中所有函数的字典
        """
        return self.__foos